package com.example.webwork.util;


import org.springframework.stereotype.Service;


public class SnowflakeIdWorker {
    // 时间起始标记点，作为基准时间戳
    private final long twepoch = 1288834974657L;

    // 机器ID所占的位数
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    // 毫秒内自增位所占的位数
    private final long sequenceBits = 12L;

    // 最大序列值
    private final long maxSequence = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastWorkerId = 0L;
    private long lastDatacenterId = 0L;

    public SnowflakeIdWorker(long workerId, long datacenterId) {
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = 0L;
        this.lastTimestamp = -1L;
    }

    public synchronized long nextId(int userId) {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    "Clock moved backwards. Refusing to " + (lastTimestamp - timestamp) + " milliseconds");
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0) {
                timestamp++;
                sequence = 0L;
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        // 将 userId 的低位作为序列号的补充，增加唯一性
        long combinedId = ((timestamp - twepoch) << (workerIdBits + datacenterIdBits + sequenceBits))
                | (datacenterId << (workerIdBits + sequenceBits))
                | (workerId << sequenceBits)
                | (sequence);

        // 使用 userId 对 combinedId 进行异或操作，增强唯一性和分布散列
        return (combinedId & ~0xFFL) | (userId & 0xFF); // 用 userId 的最低6位替换掉最后6位    }
    }

    protected long timeGen() {
        return System.currentTimeMillis() * 1000;
    }
}